/**
 * 封装全局接口请求axios函数
 */

import axios from 'axios'
import JSONBIGINT from 'json-bigint'
// 导入store=》获取vuex实例=》作用和组件中的$store一样
import store from '../store'
// 导入router实例=> 作用和组件中的$router一样
import router from '../router'
// console.log(store)

// 创建自己的axios实例=》axios.create工厂函数
const BASE_URL = 'http://toutiao-app.itheima.net/v1_0/'
const MyAxios = axios.create({
  // baseURL: 'http://ttapi.research.itcast.cn/app/v1_0/',
  baseURL: BASE_URL,
  /**
   * 原因：axios内部在格式化后台返回的数据的时候使用的是JSON.parse=》超出js精度范围会失真
   * 作用：json-bigint代替JSON.parse方法格式化后台返回的数据
   *
   */
  transformResponse: [function (data) {
    // console.log('后台返回的数据', data)
    // Do whatever you want to transform the data
    // data =>后台返回的JSON字符串
    try {
      // 处理JSON.parse/stringify精度丢失
      // test:console.log(JSONBIGINT.parse('{"a":9223372036854775807}').a.toString(), JSON.parse('{"a":9223372036854775807}'))
      return JSONBIGINT.parse(data)
    } catch (e) {
      return data
    }
  }
  ]

})

// 请求拦截器=》发送请求之前会执行
// 说明：请求还没发出去
// Add a request interceptor
MyAxios.interceptors.request.use(function (config) {
  // console.log('1. 开始发送请求了', config)
  // Do something before request is sent
  // 处理接口版本问题=》新闻列表请求路径是v1_1
  if (config.url === 'articles') {
    config.baseURL = config.baseURL.replace('v1_0', 'v1_1')
  }
  // 请求之前，统一向header中加入token
  // ?.user 处理null
  // console.log(store.state.user.token)
  if (store.state.user.token && config.url !== 'authorizations') {
    config.headers.Authorization = `Bearer ${store.state.user.token}`
    // console.log(config.headers)
  }
  return config
}, function (error) {
  // Do something with request error
  return Promise.reject(error)
})

// 响应拦截器=》后台响应后（返回数据）执行=》先执行它=》页面才能获取到数据
// 说明：页面还没获取到数据
// Add a response interceptor
MyAxios.interceptors.response.use(function (response) {
  // console.log('2. 请求成功了', response)
  // Any status code that lie within the range of 2xx cause this function to trigger
  // Do something with response data
  // return response
  // 处理和简化返回的数据=>返回后=》页面获取指定的数据
  return {
    status: response.status,
    data: response.data.data
  }
}, async function (error) {
  // Any status codes that falls outside the range of 2xx cause this function to trigger
  // Do something with response error
  /**
   * 处理：先抽象到具体
   * 1. 组件 =》 封装
   * 2. 封装 =》组件
   */
  console.log('捕获请求错误=》401', error.response)
  // 跳转登录页面的配置=>其中redirectUrl是上次访问的页面地址
  // 目的：登录成功后后返回到上次访问的页面
  // 通过路由实例对象router.currentRoute.path获取当前页面的路径
  const loginConfig = { path: '/login', query: { redirectUrl: router.currentRoute.path } }
  try {
    if (error && error.response.status === 401) {
      /**
       * 处理请求401错误的情况
       * 1. 没有登录过 =》跳回登录
         2. 登录过**，token过期了，但是refresh_token 没过期 =》刷新token =〉更新vuex和本地token =》继续上次401的错误请求（token已更新）
         3. token和refresh_token 都失效 =〉跳回登录
            * 通过Promise.reject(error)返回异常信息
       */
      console.log(error.response.status)
      /**
       * 系统判断是否登录？=》通过vuex获取有没有token
       */
      // 获取token(vuex)
      // 1. 没有登录过 =》跳回登录
      const { user } = store.state
      if (!user.token || !user.refresh_token) {
        // 跳回登录页面
        router.replace(loginConfig)
        // 符合这种情况直接抛出错误，阻止后续代码的执行
        return Promise.reject(error)
      }
      // 2. 登录过，token过期了=>刷新token=》拿refresh_token调用后台接口获取新token
      const { status, data } = await MyAxios.put('authorizations', null, {
        headers: {
          Authorization: `Bearer ${user.refresh_token}`
        }
      })
      console.log('获取新token：', status, data)
      if (status === 201) {
        // 1. 更新vuex中存储的token=》过期的换成新的token
        store.commit('setToken', {
          token: data.token,
          refresh_token: user.refresh_token
        })
        // 2. 重新上次401请求（这时候会带上新token）
        return MyAxios(error.config)
      }
    }
  } catch (error) {
    // 3. token和refresh_token 都失效 =〉跳回登录
    console.log('token和refresh_token 都失效 =〉跳回登录')
    // 跳回登录页面
    router.replace(loginConfig)
    // 符合这种情况直接抛出错误，阻止后续代码的执行
    return Promise.reject(error)
  }
  return Promise.reject(error)
})

// 插件封装
/**
 * 一种高级玩法/用法
 * @param {*} Vue 注册的时候Vue.use调用咱们的方法传入的Vue对象
 * 目的：方便管理和维护
 */
const MyPlugs = function (Vue) {
  Vue.prototype.request = MyAxios
}

export { MyPlugs }

// 在js中使用这个导入发请求
export default MyAxios
